package cn.zhaosunny.soap

import cn.zhaosunny.soap.internal.SoapHttp
import io.reactivex.BackpressureStrategy
import io.reactivex.Flowable
import io.reactivex.Observable
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.suspendCancellableCoroutine
import java.lang.reflect.Type
import kotlin.coroutines.Continuation
import kotlin.coroutines.resumeWithException

/**
 *
 * @author zhaoyang 2021/11/30
 */

abstract class CallFactory {

    var kotinRawType: Type? = null
    var soapRequest: SoapRequest? = null
    var soap: Soap? = null

    open fun call(soap: Soap, soapRequest: SoapRequest): String {
        return SoapHttp.call(soap, soapRequest)
    }


    open fun observableCall(
        soap: Soap,
        soapRequest: SoapRequest,
        returnType: Type,
    ): Observable<Any> {
        return Observable.create {
            val result = call(soap, soapRequest)
            val convertResult = soap.convertResult(result, returnType)
            if (!it.isDisposed) {
                it.onNext(convertResult)
            }
            it.onComplete()
        }
    }

    open fun flowableCall(
        soap: Soap,
        soapRequest: SoapRequest,
        returnType: Type,
    ): Flowable<Any> {
        return Flowable.create({
            val result = call(soap, soapRequest)
            val convertResult = soap.convertResult(result, returnType)
            if (!it.isCancelled) {
                it.onNext(convertResult)
            }
            it.onComplete()
        }, BackpressureStrategy.LATEST)
    }

    open fun flowCall(
        soap: Soap,
        soapRequest: SoapRequest,
        returnType: Type,
    ): Flow<Any> {
        return flow {
            val result = call(soap, soapRequest)
            val convertResult = soap.convertResult(result, returnType)
            emit(convertResult)
        }

    }

    fun kotlinCall(): Any? {
        val result = call(soap!!, soapRequest!!)
        return soap!!.convertResult(result, kotinRawType!!)
    }

    @ExperimentalCoroutinesApi
    open suspend fun suspendCall(
        callFactory: CallFactory,
        soap: Soap,
        soapRequest: SoapRequest,
        type: Type,
    ): Any? {
        return suspendCancellableCoroutine { continuation ->
            continuation.invokeOnCancellation {
                //TODO Soap停止方法，暂不支持，okhttp3 Call有cancel方法
            }
            try {
                callFactory.setSoap(soap, soapRequest, type)
                val kotlinCall = callFactory.kotlinCall()
                continuation.resume(kotlinCall) {

                }
            } catch (e: Throwable) {
                continuation.resumeWithException(e)
            }
        }

    }


    open fun setSoap(soap: Soap, soapRequest: SoapRequest, kotinRawType: Type) {
        this.soap = soap
        this.soapRequest = soapRequest
        this.kotinRawType = kotinRawType
    }

    fun call(
        continuation: Continuation<Any?>?,
        soap: Soap,
        soapRequest: SoapRequest?,
        type: Type?,
    ): Any? {
        return InternalSoapCall.call(continuation, soap, soapRequest, type)
    }

}


